#include "header/BTree.h"
#include "header/Librarian.h"

/**
 * @brief 把整形(int)转换成字符串(char*)的形式
 * 
 * @param num       整形数字
 * @return char*    字符串形式
 */
char *int2str(int num){
    char *res = (char *)malloc(sizeof(char) * 50);
    itoa(num, res, 10);
    return res;
}

/**
 * @brief Get the Book object
 * 
 * @param name 
 * @param writer 
 * @param total 
 * @return bookptr 
 */
bookptr getBook(int bookId, char *name, char *writer, int total){
    bookptr p = (bookptr)malloc(sizeof(book));
    p->bookId = bookId;
    p->count = p->total = total;
    strcpy(p->name, name);
    strcpy(p->writer, writer);
    initQueue(&(p->borrower));
    return p;
}

/**
 * @brief 初始化队列
 * 
 * @param q 
 */
void initQueue(queue *q){
    (*q) = (queue)malloc(sizeof(que));
    (*q)->count = 0;
    (*q)->rear = (*q)->front = NULL;
}

/**
 * @brief 入队
 * 
 * @param q     队列
 * @param rdr   the node data
 * @return int  1-success 0-error
 */
int enQueue(queue q, rcdptr rdr){
    if (q == NULL || rdr == NULL){
        return 0;
    }
    // save the node data
    qNode *node = (qNode *)malloc(sizeof(qNode));
    node->data = rdr;
    node->next = NULL;
    if (q->rear){
        q->rear->next = node;
    }
    q->rear = node;
    if (q->front == NULL){
        q->front = node;
    }
    q->count++;
    return 1;
}

/**
 * @brief 出队
 * 
 * @param q 队列
 */
void deQueue(queue q){
    if(q == NULL || q->front == NULL){
        return;
    }
    qNode *p = q->front;
    q->front = p->next;
    free(p);
    p = NULL;
    q->count--;
}

/**
 * @brief Get the Front object
 * 
 * @param q         the queue
 * @return rcdptr   the front data of the queue
 */
rcdptr getFront(queue q){
    if(q == NULL || q->front == NULL){
        return NULL;
    }
    return q->front->data;
}

/**
 * @brief remove the elem of the queue by readerId
 * 
 * @param q         the queue
 * @param readerId  借阅者id
 * @return int      success return 1 else return 0
 */
int removeElem(queue q, int readerId){
    if(q == NULL || q->front == NULL){
        return 0;
    }
    qNode *p = q->front;
    if(p->data->reader->readerId == readerId){
        q->front = p->next;
        free(p);
        q->count--;
        return 1;
    } else {
        while(p->next){
            if(p->next->data->reader->readerId == readerId){
                qNode *temp = p->next;
                p->next = temp->next;
                free(temp);
                q->count--;
                return 1;
            }
            p = p->next;
        }
    }
    return 0;
}

/**
 * @brief 菜单
 * 
 */
void menu(){
        printf("\n                 ||****************************************MENU****************************************||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||*********************************Librarian System***********************************||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||====================================================================================||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Please enter your choice<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||====================================================================================||\n");
        printf("                 ||                     1.Add Book                   2.Add Reader                      ||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||                     3.Delete Book                4.Update Book                     ||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||                     5.Check Book Detail Message  6.Print Tree Struction            ||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||                     7.Find Book List of Writer   8.Check Book List                 ||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||                     9.Check Reader List          10.Destory BTree                  ||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||                     11.Borrow Book               12.Return Book                    ||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||                     13.Exit                                                        ||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||====================================================================================||\n");
        printf("                 ||                                                                                    ||\n");
        printf("                 ||****************************************END*****************************************||\n");
        printf("                 ||->>      Please input your option:");
}

/**
 * @brief fulsh
 * 
 */
void flush(){
    char c;
    while ((c = getchar()) != 10 && c != EOF);
}

/**
 * @brief 检查输入并插入数据
 * 
 * @param t         BTree
 * @param table     hashtable
 * @param k         关键字k
 * @param rec 
 */
int  checkAndInsert(BTree *t, Hashptr table, KeyType k, void *rec){
    resultptr p = searchBTree(*t, k);
    if (p->tag){
        printf("\n\n\n\n\n||->>                         insert book failed! %d exist\n", k);
        free(p);
        return 0;
    } else {
        insertBTree(t, k, p->pt, p->i, rec);
        // add hash
        addHash(table, (bookptr)rec);
        time_t t = time(NULL);
        char time[25];
        // 格式化时间
        strftime(time, 25, "%Y-%m-%d %H:%M:%S", localtime(&t));
        char *msg = catchString(4, time, "      Add new book ", ((bookptr)rec)->name, "\n");
        // 打印日志
        writeLog(msg);
        free(msg);
    }
    free(p);
    return 1;
}

/**
 * @brief 打印所有图书信息
 * 
 * @param t BTree
 */
void printAllBook(BTree t){
    printf("\n\t--------------------------------------------------------------------------------------------\n");
    printf("\t|   bookId    |         bookName      |        bookWriter     |   bookCount  |   bookTotal |\n");
    printf("\t--------------------------------------------------------------------------------------------\n");
    printBookIndex(t);
}

/**
 * @brief 打印所有图书信息-功能实现类
 * 
 * @param t BTree
 */
void printBookIndex(BTree t){
    if (t){
        printBookIndex(t->ptr[0]);
        for (int i = 1; i <= t->keynum; i++){
            printBook((bookptr)(t->record[i]));
            printBookIndex(t->ptr[i]);
        }
    }
}

/**
 * @brief 打印图书的详细信息（借阅情况）
 * 
 * @param book book data
 */
void printBookDetail(bookptr book){
    printf("\n\t--------------------------------------------------------------------------------------------\n");
    printf("\t|   bookId    |         bookName      |        bookWriter     |   bookCount  |   bookTotal |\n");
    printf("\t--------------------------------------------------------------------------------------------\n");
    // 图书信息
    printCompletBook(book);

    printf("\tThe borrower list:\n");
    qNode *qp = book->borrower->front;
    printf("\t---------------------------------------------------------------------------------\n");
    printf("\t|     Id      |     Name     |        BorrowTime       |         ReturnTime     |\n");
    printf("\t---------------------------------------------------------------------------------\n");
    // 打印借阅记录信息
    while (qp){
        readerptr rdr = qp->data->reader;
        printf("\t|  %-8d   |   %-8s   |   %s   |   %s  |\n", rdr->readerId, rdr->name, qp->data->borrowTime, qp->data->returnTime);
        printf("\t---------------------------------------------------------------------------------\n");
        qp = qp->next;
    }

}

/**
 * @brief 打印单条图书信息
 * 
 * @param book book
 */
void printCompletBook(bookptr book){
    printf("\t|  %-8d   |   %-15s     |   %-15s     |   %-8d   |   %-8d  |\n", book->bookId, book->name, book->writer, book->count, book->total);
    printf("\t--------------------------------------------------------------------------------------------\n");
}

/**
 * @brief 打印单条图书信息2
 * 
 * @param book 
 */
void printBook(bookptr book){
    printf("\t|  %-8d   |   %-15.15s     |   %-15.15s     |   %-8d   |   %-8d  |\n", book->bookId, book->name, book->writer, book->count, book->total);
    printf("\t--------------------------------------------------------------------------------------------\n");
}

/**
 * @brief 打印某作者的所有图书信息
 * 通过hash 作者名存储图书数据，可以直接找出该作者的所有图书作品信息
 * 
 * @param table hashtable
 * @param name  writer name
 * @return int  success return 1 else return error
 */
int printBookByWriter(Hashptr table, char *name){
    if (table == NULL){
        return 0;
    }
    // get index
    int index = hashFunc(name) % table->size;
    bookNodeptr p = table->rcd[index];
    printf("\n\t--------------------------------------------------------------------------------------------\n");
    printf("\t|   bookId    |         bookName      |        bookWriter     |   bookCount  |   bookTotal |\n");
    printf("\t--------------------------------------------------------------------------------------------\n");
    while (p){
        printBook(p->data);
        p = p->next;
    }
    return 1;
}

/**
 * @brief 更新图书信息
 * 
 * @param tb        hashtable point
 * @param book      book point
 * @param nm        name
 * @param wrtr      writer
 * @param tot       total
 * @return int success return 1 else return 0
 */
int updateBook(Hashptr tb, bookptr book, char *nm, char *wrtr, int tot){

    // 新的总量小于剩余量时更新失败
    if(tb == NULL || book == NULL || !nm || !wrtr || tot < (book->total - book->count)){
        return 0;
    }
    removeHash(tb, book);
    strcpy(book->name, nm);
    strcpy(book->writer, wrtr);
    // 新增的, 更新库存和总库存
    int inc = tot - book->total;
    book->count += inc;
    book->total = tot;
    // update hash data
    addHash(tb, book);
    return 1;
}

/**
 * @brief 保存数据至文件
 * 
 * @param t     book   BTree
 * @param rt    reader Btree
 */
void save(BTree t, BTree rt){
    FILE *fp_index = fopen("index.dat", "wb");
    FILE *fp_queue = fopen("queue.dat", "wb");
    FILE *fp_reader = fopen("reader.dat", "wb");
    saveBTree(t, fp_index, fp_queue);
    saveReader(rt, fp_reader);
    fclose(fp_reader);
    fclose(fp_index);
    fclose(fp_queue);
}

/**
 * @brief 保存图书信息
 * 
 * @param t         BTree
 * @param index     file point index
 * @param queue     file point queue
 */
void saveBTree(BTree t, FILE *index, FILE *queue){
    if(t == NULL){
        return;
    }
    bookptr book;
    BTree visit;
    for (int i = 1; i <= t->keynum; i++){
        book = (bookptr)(t->record[i]);
        if(book == NULL || book->bookId == 0){
            continue;
        }
        // 向index.dat中写入book data
        fwrite(book, sizeof(struct book), 1, index);
        int count = book->borrower->count;
        // 向queue.dat写入借阅数量
        fwrite(&count, sizeof(int), 1, queue);
        qNode *p = book->borrower->front;
        while (p){
            // 向queue.dat写入借阅信息
            fwrite(p->data->reader, sizeof(reader), 1, queue);
            fwrite(p->data->borrowTime, sizeof(char) * 30, 1, queue);
            fwrite(p->data->returnTime, sizeof(char) * 30, 1, queue);
            p = p->next;
        }
    }
    for (int i = 0; i <= t->keynum && t->ptr[i]; i++){
        // 向下层递归save
        visit = t->ptr[i];
        saveBTree(visit, index, queue);
    }
}

/**
 * @brief 
 * 
 * @param rt 
 * @param fp_reader 
 */
void saveReader(BTree rt, FILE *fp_reader){
    if (rt == NULL){
        return;
    }
    readerptr rdr = NULL;
    // 访问节点
    BTree visit = NULL;
    // 向reader.dat写入借阅者信息
    for (int i = 1; i <= rt->keynum; i++){
        rdr = (readerptr)(rt->record[i]);
        fwrite(rdr, sizeof(reader), 1, fp_reader);
    }
    // 递归保存借阅者信息
    for (int i = 0; i <= rt->keynum && rt->ptr[i]; i++){
        visit = rt->ptr[i];
        saveReader(visit, fp_reader);
    }

}

/**
 * @brief 从文件中加载数据
 * 
 * @param t     book BTree
 * @param rt    reader BTree
 * @param tb    hashtable
 * @return int  success return 1 else return 0
 */
int load(BTree *t, BTree *rt, Hashptr tb){
    if(t == NULL || rt == NULL || tb == NULL){
        return 0;
    }
    FILE *fp_index = fopen("index.dat", "rb");
    FILE *fp_reader = fopen("queue.dat", "rb");
    // not file point
    if(!fp_index || !fp_reader){
        return 0;
    }
    // load reader data
    loadReader(rt);
    // 重名
    bookptr book = (bookptr)malloc(sizeof(struct book));
    int result = 0;
    readerptr rdp = (readerptr)malloc(sizeof(reader));
    // load book data
    while(fread(book, sizeof(struct book), 1, fp_index)){
        result = loadBTree(t, book);
        addHash(tb, book);
        // 借阅队列
        initQueue(&(book->borrower));
        int cnt = 0;
        fread(&cnt, sizeof(int), 1, fp_reader);
        while(cnt-- >= 0){
            // load record 
            rcdptr rc = (rcdptr)malloc(sizeof(rcd));
            fread(rdp, sizeof(reader), 1, fp_reader);
            fread(rc->borrowTime, sizeof(char) * 30, 1, fp_reader);
            fread(rc->returnTime, sizeof(char) * 30, 1, fp_reader);
            resultptr res = searchBTree(*rt, rdp->readerId);
            if(res->tag == 1){
                rc->reader = (readerptr)(res->pt->record[res->i]);
                enQueue(book->borrower, rc);
            } else {
                // 已经load了，rt里应该都有
                insertReader(rt, rdp);
                rc->reader = rdp;
                enQueue(book->borrower, rc);
                rdp = (readerptr)malloc(sizeof(reader));
            }
        }
        book = (bookptr)malloc(sizeof(struct book));
    }
    free(rdp);
    free(book);
    fclose(fp_reader);
    fclose(fp_index);
    return result;
}

/**
 * @brief 加载借阅者数据
 * 
 * @param rt    BTree point of reader
 * @return int  success return 1 else return 0
 */
int loadReader(BTree *rt){
    FILE *fp_reader = fopen("queue.dat", "rb");
    // not exist the file
    if(fp_reader == NULL){
        return 0;
    }
    readerptr reader = (readerptr)malloc(sizeof(struct reader));
    // overflow
    if(reader == NULL){
        return 0;
    }
    int res = 0;
    // 先查找，找不到再插入
    while(fread(reader, sizeof(reader), 1, fp_reader)){
        resultptr result = (resultptr) searchBTree(*rt, reader->readerId);
        // not find
        if(result->tag == 0){
            res = insertBTree(rt, reader->readerId, result->pt, result->i, reader);
            reader = (readerptr)malloc(sizeof(struct reader));
        }
    }
    // close the file stream
    fclose(fp_reader);
    free(reader);
    return res;
}

/**
 * @brief 加载图书数据
 * 
 * @param t     BTree
 * @param book  book data point
 * @return int  success return 1 else return 0
 */
int loadBTree(BTree *t, bookptr book){
    resultptr res = searchBTree(*t, book->bookId);
    int result = 1;
    if (res->tag == 0){
        result = (insertBTree(t, book->bookId, res->pt, res->i, book) == SUCCESS);
    }
    return result;
}

/**
 * @brief 拼接字符串
 * 
 * @param n         数目
 * @param ...       可变参
 * @return char*    
 */
char *catchString(int n, ...){
    // 获取变参
    va_list list;
    // 读取变参，遍历和读取栈区中的参数列表
    va_start(list, n);
    char *msg = (char *)malloc(sizeof(char) * 70);
    char *p = NULL;
    int i = 0;
    for (; i < 70; i++){
        msg[i] = 0;
    }
    while (n--){
        // 使list指针指向变参列表中的下一个参数, 并返回其值
        p = va_arg(list, char *);
        // 拼接字符串
        strcat(msg, p);
    }
    // 允许函数返回
    va_end(list);
    return msg;
}

/**
 * @brief 打印日志
 * 
 * @param msg msg
 */
void writeLog(char *msg){
    FILE *p = fopen("Librarian.log", "a");
    if (p){
        fputs(msg, p);
    }
    fclose(p);
}


/**
 * @brief hash
 * 
 * @param table hashtable
 * @param book  图书信息
 * @return int  if exist return 0 else return 1
 */
int addHash(Hashptr table, bookptr book){
    if (table == NULL || book == NULL){
        return 0;
    }
    // check if is exist
    if (existHash(table, book)){
        return 0;
    }
    int index = hashFunc(book->writer) % table->size;
    bookNodeptr p = (bookNodeptr)malloc(sizeof(bookNode));
    // save data to hashtable
    p->data = book;
    p->next = table->rcd[index];
    table->rcd[index] = p;
    table->count++;
    return 1;
}

/**
 * @brief check is exist hash
 * 
 * @param table hashtable
 * @param book  data
 * @return int  1-exist 0-unexist
 */
int existHash(Hashptr table, bookptr book){
    //  hash
    int index = hashFunc(book->writer) % table->size;
    bookNodeptr p = table->rcd[index];
    if (p == NULL){
        return 0;
    }
    while (p->next){
        if (p->next->data->bookId == book->bookId){
            return 1;
        }
        p = p->next;
    }
    return 0;
}

/**
 * @brief hash func
 * 
 * @param data      hash data
 * @return int      result
 */
int hashFunc(char *data){
    int t = 0;
    for (int i = 0; data[i];i++){
        t += data[i];
    }
    return t ^ (t >> 16);
}

/**
 * @brief 移除hash
 * 
 * @param table hashtable
 * @param book  book data
 * @return int  success return 1 else return 0
 */
int removeHash(Hashptr table, bookptr book){
    // get index
    int index = hashFunc(book->writer) % table->size;
    bookNodeptr p = table->rcd[index];
    if (p == NULL){
        return 0;
    }
    // find it
    if (p->data->bookId == book->bookId){
        bookNodeptr temp = p;
        table->rcd[index] = p->next;
        free(temp);
        return 1;
    }
    // not find but can find the next to find it
    while (p->next){
        if (p->next->data->bookId == book->bookId){
            bookNodeptr temp = p->next;
            p->next = temp->next;
            free(temp);
            return 1;
        }
        p = p->next;
    }
    return 0;
}

/**
 * @brief 新增借阅者信息
 * 
 * @param t     BTree
 * @param rec   reader data point
 */
int insertReader(BTree *t, void *rec){
    readerptr rdp = (readerptr)rec;
    resultptr res = searchBTree(*t, rdp->readerId);
    // 如果找到
    if(res->tag){
        printf("\n\n\n\n\n||->>                         insert reader failed! %d exist\n", rdp->readerId);
        free(res);
        return 0;
    } else {
        insertBTree(t, rdp->readerId, res->pt, res->i, rec);
        time_t t = time(NULL);
        char time[25];
        // format time style
        strftime(time, 25, "%Y-%m-%d %H:%M:%S", localtime(&t));
        char id[10];
        itoa(rdp->readerId, id, 10);
        char *msg = catchString(4, time, "      Add new reader ", id, "\n");
        // write log
        writeLog(msg);
        free(msg);
        free(res);
        return 1;
    }
}

/**
 * @brief 打印所有借阅者信息
 * 
 * @param rt BTree
 */
void printAllReader(BTree rt){
    printf("\t------------------------------\n");
    printf("\t|     Id      |     Name     |\n");
    printf("\t------------------------------\n");
    printReaderIndex(rt);
}

/**
 * @brief 打印所有借阅者信息-功能信息
 * 
 * @param rt BTree
 */
void printReaderIndex(BTree rt){
    if (rt){
        printReaderIndex(rt->ptr[0]);
        int i = 1;
        for (; i <= rt->keynum; i++){
            readerptr rdr = (readerptr)(rt->record[i]);
            printf("\t|  %-8d   |   %-8s   |\n", rdr->readerId, rdr->name);
            printf("\t------------------------------\n");
            // 递归打印子节点
            printReaderIndex(rt->ptr[i]);
        }
    }
}

/**
 * @brief 借书
 * 
 * @param book  book data 
 * @param rdr   reader data point
 * @return int success return 1 else return 0
 */
int borrowBook(bookptr book, readerptr rdr){
    // 库存不足
    if (book == NULL || book->count <= 0){
        return 0;
    }
    rcdptr rc = (rcdptr)malloc(sizeof(rcd));
    rc->reader = rdr;
    time_t t = time(NULL);
    strftime(rc->borrowTime, 25, "%Y-%m-%d %H:%M:%S", localtime(&t));
    // 借书时限为30天
    time_t t2 = t + 30 * 24 * 60 * 60;
    strftime(rc->returnTime, 25, "%Y-%m-%d %H:%M:%S", localtime(&t2));
    enQueue(book->borrower, rc);
    book->count--;
    return 1;
}

/**
 * @brief 归还图书
 * 
 * @param book      book data
 * @param readerid  借阅者id
 * @return int success return 1 else return 0
 */
int returnBook(bookptr book, int readerid){
    if (book == NULL){
        return 0;
    }
    if (removeElem(book->borrower, readerid)){
        book->count++;
        return 1;
    }
    return 0;
}


